package com.hup.utils.swing;

import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.exception.ErrInfo;
import com.hup.utils.commons.thread.ThreadPoolUtil;
import lombok.extern.log4j.Log4j2;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;

/**
 * 图像缓存与异步加载器
 *
 * @author hugan
 * @date 2020/5/20
 */
@Log4j2
public class ImageCacher {

    private final ExecutorService executor = ThreadPoolUtil.imageExecutor(ImageCacher.class.getSimpleName() + "-", "图像缓存器");
    private final HashMap<String, ImageInfo> images = new HashMap<>();

    private final ArrayList<ImageCacherObserver> loadings = new ArrayList<>();

    /**
     * 获取图像;
     *
     * @return notNull-缓存数据;
     * null-无缓存数据,数据将通过 {@link ImageCacherObserver}进行异步加载和缓存,最后通知所有历史请求过该key图像的 observer
     */
    public ImageInfo getImage(ImageCacherObserver observer) {
        String key = observer.getKey();
        synchronized (images) {
            if (images.containsKey(key)) {
                log.debug("有缓存={}", key);
                return images.get(key);
            }
        }

        //没有缓存:异步加载图像
        log.debug("无缓存={}", key);
        /*
        是否已有observer在加载本图像
        初始化值应为false(loadings可能为空)
         */
        boolean hadLoadingTask = false;
        log.debug("loadings-查询有无任务.st={}", key);
        synchronized (loadings) {
            log.debug("loadings-查询有无任务.in={}, loadings={}", key, loadings.size());
            for (ImageCacherObserver loading : loadings) {
                hadLoadingTask = loading.getKey().equals(key);
                if (hadLoadingTask) break;
            }
            //一律添加到loadings
            loadings.add(observer);
        }

        if (!hadLoadingTask) {
            /*
            这个图像,没有任何对象在创建
            异步任务:
                .创建图像
                .添加到缓存中
                .通知所有匹配的observer
             */
            executor.execute(() -> {
                log.debug("启动异步加载任务={}", key);
                ImageInfo res;
                try {
                    res = observer.createImage();
                } catch (Throwable e) {
                    ErrInfo errInfo = AppExceptionHandler.handle("加载异常:", e);
                    res = new ImageInfo(null, errInfo.getErrMsg());
                }

                log.debug("加载完成={}", key);
                synchronized (images) {
                    //马上加入到缓存,下面遍历通知时,不影响新的getImage
                    images.put(key, res);
                }

                log.debug("loadings-通知observer.st={}", key);
                synchronized (loadings) {
                    log.debug("loadings-通知observer.in={}, loadings={}", key, loadings.size());
                    int width = 0;
                    int height = 0;
                    if (res.image != null) {
                        width = res.image.getWidth();
                        height = res.image.getHeight();
                    }
                    Iterator<ImageCacherObserver> it = loadings.iterator();
                    while (it.hasNext()) {
                        ImageCacherObserver loading = it.next();
                        if (key.equals(loading.getKey())) {
                            loading.onImageCreated(res, width, height);
                            it.remove();
                        }
                    }
                }
            });
            return null;
        } else {
            log.debug("等待异步通知={}", key);
            /*
            这个图像,已经在创建了,但未创建完(images中没有它)
            前面已经将 observer添加在loadings中了
             */
            return null;
        }
    }

    public void flush() {
        synchronized (images) {
            for (Entry<String, ImageInfo> entry : images.entrySet()) {
                entry.getValue().image.flush();
            }
            images.clear();
        }
    }

    public void flush(String key) {
        synchronized (images) {
            if (images.containsKey(key)) {
                images.remove(key).image.flush();
            }
        }
    }

    public static class ImageInfo {
        /**
         * nullable
         */
        public final BufferedImage image;
        /**
         * 在 {@link ImageCacherObserver#createImage()}中产生的错误信息,nullable
         */
        public final String errMsg;

        public ImageInfo(BufferedImage image, String errMsg) {
            this.image = image;
            this.errMsg = errMsg;
        }
    }

    public interface ImageCacherObserver {
        /**
         * 该图像的唯一标识
         *
         * @return notNull
         */
        String getKey();

        /**
         * 创建图像
         */
        ImageInfo createImage();

        void onImageCreated(ImageInfo imageInfo, int width, int height);
    }

}
